import java.io.File;
import java.util.TreeMap;
import org.jfugue.*;
import java.io.*;

public class MCS {

    // Ordered by http://www.cryptograms.org/letter-frequencies.php
    static String[] orderedLetters = new String[] {" ", "e", "t", "a", "o", "i", "n", "s", "h", "r", "d", "l", "u", "c", "m", "f", "w", "g", "y", "p", "b", "v", "k", "x", "j", "q", "z"};
    static String[] orderedBigrams = new String[] {"th", "he", "in", "en", "nt", "re", "er", "an", "ti", "es", "on", "at", "se", "nd", "or", "ar", "al", "te", "co", "de", "to", "ra", "et", "ed", "it", "sa", "em", "ro"};
    static String[] orderedTrigrams = new String[] {"the", "and", "tha", "ent", "ing", "ion", "tio", "for", "nde", "has", "nce", "edt", "tis", "oft", "sth", "men"};
    static String[] orderedQuadrigrams = new String[] {"that", "ther", "with", "tion", "here", "ould", "ight", "have", "hich", "whic", "this", "thin", "they", "atio", "ever", "from", "ough", "were", "hing", "ment"};
    static String[][] orderedLists = new String[][] {orderedLetters};

    public static void main(String[] args) {
        Player player = new Player();
        if (args[0].equals("encrypt"))
            try {
                player.saveMidi(new Pattern(encrypt(player.loadMidi(new File(args[1])).getMusicString(), args[3])), new File(args[2]));
            } catch (Exception e) {
                // oh well
            }
        else
            try {
                BufferedWriter out = new BufferedWriter(new FileWriter(args[2]));
                out.write(decrypt(player.loadMidi(new File(args[1])).getMusicString(), player.loadMidi(new File(args[3])).getMusicString()));
                out.close();
            } catch (Exception e) {
                // lol
            }
        System.exit(0);
        player.close();
    }

    public static String encrypt(String musicString, String plainText/*, boolean aesthetic*/) {
        String cipherText = plainText.toLowerCase().replaceAll("[^a-zA-Z\\s]", "");
        /*if (aesthetic) {
        orderedLists = new String[5][];
        for (int i = 0; i < orderedLists.length; i++) orderedLists[i] = letterFrequencies(cipherText, orderedLists.length - i).descendingKeySet().toArray(new String[] {});
        }*/
        String[] score = getScore(musicString);
        for (int i = 0; i < orderedLists.length; i++) {
            TreeMap<String, Integer> noteFrequencies = noteFrequencies(score, orderedLists.length - i);
            for (int j = 0; j < orderedLists[i].length; j++) {
                cipherText = cipherText.replaceAll(orderedLists[i][j], noteFrequencies.lastKey());
                if (noteFrequencies.size() > 1) noteFrequencies.remove(noteFrequencies.lastKey());
            }
        }
        return cipherText;
    }

    public static String decrypt(String musicString, String cipherText) {
        String plainText = getNotes(cipherText);
        String[] score = getScore(musicString);
        for (int i = 0; i < orderedLists.length; i++) {
            TreeMap<String, Integer> noteFrequencies = noteFrequencies(score, orderedLists.length - i);
            for (int j = 0; j < orderedLists[i].length; j++) {
                plainText = plainText.replaceAll(noteFrequencies.lastKey().toUpperCase(), orderedLists[i][j]);
                if (noteFrequencies.size() > 1) noteFrequencies.remove(noteFrequencies.lastKey());
            }
        }
        return plainText;
    }

    public static String getNotes(String musicString) {
        String notes = "";
        for (String s : musicString.toUpperCase().split("\\s+"))
            try {
                if (isToken(MusicStringParser.getNote(s).getMusicString())) notes += s.split("\\d")[0] + s.replaceAll("\\D", "").substring(0, 1) + "S ";
            } catch (Exception e) {
                // sucks
            }
        return notes;
    }

    public static String[] getScore(String musicString) {
        return getNotes(musicString).split("\\s+");
    }

    public static boolean isToken(String s) {
        s = s.toUpperCase();
        return (!s.equals("C0/0.0")) && ((s.startsWith("A")) || (s.startsWith("B")) || (s.startsWith("C")) || (s.startsWith("D")) || (s.startsWith("E")) || (s.startsWith("F")) || (s.startsWith("G")));
    }

    public static TreeMap<String, Integer> letterFrequencies(String line, Integer width) {
        TreeMap<String, Integer> frequencies = new TreeMap<String, Integer>();
        for (int i = 0; i < line.length(); i++)
            if (i < line.length() - width + 1) {
                String phrase = line.substring(i, i + width);
                if (frequencies.containsKey(phrase)) frequencies.put(phrase, frequencies.get(phrase) + 1);
                else frequencies.put(phrase, 1);
            }
        return frequencies;
    }

    public static TreeMap<String, Integer> noteFrequencies(String[] score, Integer width) {
        TreeMap<String, Integer> frequencies = new TreeMap<String, Integer>();
        for (int i = 0; i < score.length; i++)
            if (i < score.length - width + 1) {
                String phrase = "";
                for (int j = 0; j < width; j++) phrase += score[i + j] + " ";
                if (frequencies.containsKey(phrase)) frequencies.put(phrase, frequencies.get(phrase) + 1);
                else frequencies.put(phrase, 1);
            }
        return frequencies;
    }
}

